#include "http_server.h"
#include "esp_timer.h"

#include "esp_log.h"
#define TAG "httpd"
#define log_e(...) ESP_LOGE(TAG, __VA_ARGS__)
#define log_i(...) ESP_LOGI(TAG, __VA_ARGS__)
#define log_w(...) ESP_LOGW(TAG, __VA_ARGS__)

#ifdef CONFIG_SPIRAM
#define _malloc_(size) heap_caps_malloc(size, MALLOC_CAP_SPIRAM)
#else
#define _malloc_(size) malloc(size)
#endif

http_server_t http_server = NULL;

#if HTTPD_SERVER_ENABLE_WS
static void ws_alive_ping_tick(void *arg)
{
    http_server_t server = (http_server_t)arg;
    http_server_ws_clinet_t client = server->ws.clients;
    uint64_t time = esp_log_timestamp();
    while (client)
    {
        if (client->alive_time <= time)
        {
            // log_i("wss alive ping sock(%d)......", client->fd);
            httpd_ws_frame_t ws_pkt = {0};
            ws_pkt.type = HTTPD_WS_TYPE_PING;
            if (httpd_ws_send_frame_async(server->server, client->fd, &ws_pkt) != ESP_OK)
                break;
        }
        client = client->next;
    }
}
#endif

void httpd_close_fd(httpd_handle_t hd, int sockfd)
{
    log_i("Client disconnected %d", sockfd);
    close(sockfd);
#if HTTPD_SERVER_ENABLE_WS
    http_server_t server = httpd_get_global_user_ctx(hd);
    http_server_ws_clinet_t client = server->ws.clients, last = server->ws.clients;
    while (client)
    {
        if (client->fd == sockfd)
        {
            if (last == client)
                server->ws.clients = client->next;
            else
                last->next = client->next;
            server->ws.length--;
            log_w("Free wss sock %d, length : %d", client->fd, server->ws.length);
            memset(client, 0, sizeof(*client));
            free(client);
            break;
        }
        last = client;
        client = client->next;
    }
#endif
}

void http_server_start(char *base_path)
{
    if (!http_server)
    {
        http_server_t _http_server_ = (http_server_t)_malloc_(sizeof(http_server_s));
        if (!_http_server_)
        {
            log_e("Failed to malloc http server object buffer.(%d)", sizeof(http_server_s));
            return;
        }
        memset(_http_server_, 0, sizeof(http_server_s));

        httpd_config_t config = HTTPD_DEFAULT_CONFIG();
        config.uri_match_fn = httpd_uri_match_wildcard;
        config.core_id = 0;
        // config.task_priority = 20;
        config.stack_size = 3072 + 256; /* 1.22: 3072够用 */
        config.max_uri_handlers = 32;
        config.max_resp_headers = 32;
        config.recv_wait_timeout = 10;
        config.send_wait_timeout = 10;
        config.keep_alive_enable = true;
        config.close_fn = httpd_close_fd;
        config.global_user_ctx = _http_server_;
        log_i("Starting HTTP Server on port: '%d'", config.server_port);
        esp_err_t err = httpd_start(&_http_server_->server, &config);
        if (err != ESP_OK)
        {
            log_e("Failed to start web server!(%s)", esp_err_to_name(err));
            return;
        }

        /* URI handler for uploading files to server */
        /* 文件管理页面和API */
        // extern void file_manager_server_init(httpd_handle_t server, char *base_path);
        // file_manager_server_init(_http_server_->server, "/www");

#ifdef CONFIG_DRIVER_USE_WIFI
        /* WIFI API */
        extern esp_err_t wifi_handle(httpd_req_t * req);
        httpd_uri_t wifi_path = {
            .uri = "/wifi", // Match all URIs of type /upload/path/to/file
            .method = HTTP_POST,
            .handler = wifi_handle,
            .user_ctx = base_path};
        httpd_register_uri_handler(_http_server_->server, &wifi_path);
#endif

#if HTTPD_SERVER_ENABLE_WS
        extern esp_err_t wss_handler(httpd_req_t * req);
        _http_server_->ws.send_lock = xSemaphoreCreateMutex();
        httpd_uri_t ws = {
            .uri = "/v1/ws",
            .method = HTTP_GET,
            .handler = wss_handler,
            .user_ctx = NULL,
            .is_websocket = true,
            .handle_ws_control_frames = true};
        httpd_register_uri_handler(_http_server_->server, &ws);

        _http_server_->ws.keep_alive_period_ms = 30000;
        _http_server_->ws.last_fd = -1;
        const esp_timer_create_args_t timer_args = {
            .callback = &ws_alive_ping_tick,
            .arg = _http_server_,
            .name = "wss_alive"};
        esp_timer_handle_t wss_alive_timer = NULL;
        ESP_ERROR_CHECK(esp_timer_create(&timer_args, &wss_alive_timer));
        ESP_ERROR_CHECK(esp_timer_start_periodic(wss_alive_timer, _http_server_->ws.keep_alive_period_ms / 3 * 1000));
#endif

        extern esp_err_t root_get_handle(httpd_req_t * req);
        httpd_uri_t root_path = {
            .uri = "/*", // Match all URIs of type /upload/path/to/file
            .method = HTTP_GET,
            .handler = root_get_handle,
            .user_ctx = "/www"};
        httpd_register_uri_handler(_http_server_->server, &root_path);

        http_server = _http_server_;

#if HTTPD_SERVER_ENABLE_AUTH
        /* 添加授权用户 */
        http_server_add_user("admin", "Ttg.123456");
#endif /* HTTPD_SERVER_ENABLE_AUTH */
    }
}
